<!doctype html>

<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <script src="../../iron-test-helpers/mock-interactions.js"></script>
  <link rel="import" href="../../test-fixture/test-fixture.html">

  <link rel="import" href="helpers.html">
  <link rel="import" href="../vaadin-grid.html">
  <link rel="import" href="../vaadin-grid-selection-column.html">
  <link rel="import" href="../vaadin-grid-filter.html">
  <link rel="import" href="../vaadin-grid-column-group.html">
</head>

<body>

  <test-fixture id="default-renderer">
    <template>
      <vaadin-grid style="width: 200px; height: 200px;" size="10">
        <vaadin-grid-column></vaadin-grid-column>
        <vaadin-grid-column></vaadin-grid-column>
      </vaadin-grid>
    </template>
  </test-fixture>

  <test-fixture id="default-template">
    <template>
      <vaadin-grid style="width: 200px; height: 200px;" size="10">
        <vaadin-grid-column>
          <template>foo</template>
        </vaadin-grid-column>
        <vaadin-grid-column>
          <template>bar</template>
        </vaadin-grid-column>
      </vaadin-grid>
    </template>
  </test-fixture>

  <test-fixture id="multiselect">
    <template>
      <vaadin-grid style="width: 200px; height: 200px;">
        <vaadin-grid-selection-column auto-select></vaadin-grid-selection-column>
        <vaadin-grid-selection-column>
          <template class="header">header</template>
          <template>[[item]]</template>
        </vaadin-grid-selection-column>
        <vaadin-grid-column-group>
          <vaadin-grid-selection-column></vaadin-grid-selection-column>
        </vaadin-grid-column-group>
        <vaadin-grid-column>
          <template class="header">
            <vaadin-grid-filter path="value" value="[[filter]]">
              <input id="filter" slot="filter" value="{{filter::input}}">
            </vaadin-grid-filter>
          </template>
          <template></template>
        </vaadin-grid-column>
      </vaadin-grid>
    </template>
  </test-fixture>

  <script>
    let grid;
    let rows;
    let cachedItems;

    describe('selection', () => {

      function configureFixturedGrid() {
        grid.dataProvider = infiniteDataProvider;
        cachedItems = grid._cache.items;
        grid.selectedItems = [cachedItems[0]];
        flushGrid(grid);
        rows = getRows(grid.$.items);
      }

      beforeEach(() => {
        grid = fixture('default-renderer');
        const cols = grid.children;
        cols[0].renderer = root => root.textContent = 'foo';
        cols[1].renderer = root => root.textContent = 'bar';

        configureFixturedGrid();
      });

      it('should set selected attribute', () => {
        expect(rows[0].hasAttribute('selected')).to.be.true;
        expect(rows[1].hasAttribute('selected')).to.be.false;
      });

      it('should deselect an equaling item', () => {
        grid.itemIdPath = 'value';
        const cells = getRowCells(rows[0]);
        grid.selectedItems = [rows[0]._item];
        grid.deselectItem({value: 'foo0'});
        expect(grid.selectedItems).to.be.empty;
      });

      describe('selectedItems', () => {
        it('should reflect changes', () => {
          grid.push('selectedItems', cachedItems[1]);
          expect(rows[1].hasAttribute('selected')).to.be.true;
        });

        it('should notify', done => {
          listenOnce(grid, 'selected-items-changed', () => done());
          grid.selectedItems = [cachedItems[1]];
        });
      });

      describe('select functions', () => {
        it('should select item', () => {
          expect(grid._isSelected(cachedItems[1])).to.be.false;
          grid.selectItem(cachedItems[1]);
          expect(grid._isSelected(cachedItems[1])).to.be.true;
        });

        it('should deselect item', () => {
          expect(grid._isSelected(cachedItems[0])).to.be.true;
          grid.deselectItem(cachedItems[0]);
          expect(grid._isSelected(cachedItems[0])).to.be.false;
        });

        it('should select multiple rows', () => {
          grid.selectItem(cachedItems[1]);
          grid.selectItem(cachedItems[2]);
          expect(grid._isSelected(cachedItems[1])).to.be.true;
          expect(grid._isSelected(cachedItems[2])).to.be.true;
        });
      });
      ['renderer', 'template'].forEach(type => {

        describe(type, () => {

          beforeEach(() => {
            if (type == 'template') {
              grid = fixture('default-template');
              configureFixturedGrid();
            }
          });

          (type == 'template' ? it : it.skip)('should reflect cell instance value', () => {
            if (type == 'template') {
              const cells = getRowCells(rows[0]);
              cells[0]._instance.selected = false;
              expect(cells[0]._instance.selected).to.be.false;
              expect(grid.selectedItems).to.be.empty;
            }
          });

          it('should bind to cells', () => {
            const cells = getRowCells(rows[0]);
            let cell = cells[0];
            let model = cell._instance ? cell._instance : grid.__getRowModel(cell.parentElement);
            expect(model.selected).to.be.true;

            cell = cells[1];
            model = cell._instance ? cell._instance : grid.__getRowModel(cell.parentElement);
            expect(model.selected).to.be.true;

            cell = getRowCells(rows[1])[0];
            model = cell._instance ? cell._instance : grid.__getRowModel(cell.parentElement);
            expect(model.selected).to.be.false;
          });

          it('should select an equaling item', () => {
            grid.itemIdPath = 'value';
            const cells = getRowCells(rows[0]);
            grid.selectedItems = [{value: 'foo0'}];
            const cell = cells[0];
            const model = cell._instance ? cell._instance : grid.__getRowModel(cell.parentElement);
            expect(model.selected).to.be.true;
          });
        });
      });
    });

    describe('multi selection column', () => {
      let headerRows;
      let firstHeaderCellContent, firstBodyCellContent;
      let selectionColumn;
      let selectAllCheckbox, firstBodyCheckbox;

      beforeEach(() => {

        grid = fixture('multiselect');

        grid.items = ['foo', 'bar', 'baz'];

        flushGrid(grid);

        selectionColumn = grid._columnTree[0][0];
        cachedItems = grid._cache.items;
        rows = getRows(grid.$.items);
        headerRows = getRows(grid.$.header);

        flushGrid(grid);

        firstHeaderCellContent = getCellContent(getRowCells(headerRows[1])[0]);
        firstBodyCellContent = getCellContent(getRowCells(rows[0])[0]);

        selectAllCheckbox = firstHeaderCellContent.children[0];
        firstBodyCheckbox = firstBodyCellContent.children[0];
      });

      it('should clean up listeners on detach', () => {
        grid.removeChild(selectionColumn);
        expect(() => grid.selectItem('foo')).not.to.throw(Error);
      });

      it('should have a checkbox in the body cell', () => {
        expect(firstBodyCheckbox.localName).to.eql('vaadin-checkbox');
      });

      it('should select item when checkbox is checked', () => {
        firstBodyCheckbox.checked = true;
        expect(grid._isSelected(cachedItems[0])).to.be.true;
      });

      it('should add the item to selectedItems when row is clicked and auto-select is enabled', () => {
        const cell = getRowCells(rows[1])[1];
        cell.click();

        expect(grid.selectedItems).to.eql([getCellContent(cell).textContent]);
      });

      it('should remove the item from selectedItems when row is clicked and auto-select is enabled', () => {
        grid.selectItem(grid.items[1]);
        const cell = getRowCells(rows[1])[1];
        cell.click();
        expect(grid.selectedItems).to.eql([]);
      });

      it('should toggle the item in selectedItems when row is clicked and auto-select is enabled', () => {
        const cell = getRowCells(rows[1])[1];
        cell.click();
        expect(grid.selectedItems).to.eql([getCellContent(cell).textContent]);
        cell.click();
        expect(grid.selectedItems).to.eql([]);
      });

      it('should have bound the body checkbox to selected items', () => {
        const selectCheckbox = firstBodyCheckbox;

        grid.push('selectedItems', cachedItems[0]);

        expect(selectCheckbox.checked).to.be.true;
      });

      it('should have a select all checkbox in the header', () => {
        expect(selectAllCheckbox.localName).to.eql('vaadin-checkbox');
      });

      it('should have class name for the select all checkbox', () => {
        expect(selectAllCheckbox.classList.contains('vaadin-grid-select-all-checkbox')).to.be.true;
      });

      it('should set selectAll when header checkbox is clicked', () => {
        selectAllCheckbox.click();
        expect(selectionColumn.selectAll).to.be.true;
      });

      it('should select all items when select all is set', () => {
        selectionColumn.selectAll = true;

        expect(grid.selectedItems).to.eql(grid.items);
      });

      it('should take filter into account when selecting all items', () => {
        grid.items = [{value: 'foo'}, {value: 'bar'}];
        grid._filters = [{path: 'value', value: 'f'}];

        selectionColumn.selectAll = true;

        expect(grid.selectedItems).to.eql([grid.items[0]]);
      });

      it('should deselect all items when select all is unset', () => {
        selectionColumn.selectAll = true;

        selectionColumn.selectAll = false;

        expect(grid.selectedItems).to.be.empty;
      });

      it('should set a copy of items when all items are selected', () => {
        selectionColumn.selectAll = true;

        grid.pop('selectedItems');

        expect(grid.items).not.to.eql(grid.selectedItems);
      });

      it('should not set selection when data provider is used', () => {
        grid.items = undefined;
        grid.dataProvider = infiniteDataProvider;

        selectionColumn.selectAll = true;

        expect(grid.selectedItems).to.be.empty;
      });

      it('should hide select all checkbox when data provider is used', () => {
        grid.items = undefined;
        grid.dataProvider = infiniteDataProvider;

        expect(selectAllCheckbox.hidden).to.be.true;
      });

      it('should show select all checkbox when items is set', () => {
        grid.items = ['foo'];

        expect(selectAllCheckbox.hidden).to.be.false;
      });

      it('should be possible to override the body template', () => {
        const secondCell = getCellContent(getRowCells(rows[0])[1]);

        expect(secondCell.innerHTML).to.eql(secondCell.textContent);
      });

      it('should be possible to override the header template', () => {
        const secondCell = getCellContent(getRowCells(headerRows[1])[1]);

        expect(secondCell.innerHTML).to.eql('header');
      });

      it('should have grid reference also when column is nested inside a group', () => {
        expect(grid._columnTree[1][2]._grid).to.eql(grid);
      });

      it('should be unchecked when nothing is selected', () => {
        expect(selectAllCheckbox.indeterminate).not.to.be.ok;
        expect(selectAllCheckbox.checked).to.be.false;
      });

      it('should have indeterminate when an item is selected', () => {
        firstBodyCheckbox.checked = true;

        expect(selectAllCheckbox.indeterminate).to.be.true;
      });

      // iOS needs both to show the indeterminate status
      it('should have indeterminate and select-all when an item is selected', () => {
        expect(selectAllCheckbox.checked).to.be.false;
        expect(selectAllCheckbox.indeterminate).not.to.be.ok;

        firstBodyCheckbox.checked = true;
        expect(selectAllCheckbox.checked).to.be.true;
        expect(selectAllCheckbox.indeterminate).to.be.true;
      });

      it('should have indeterminate true when an item is deselected', () => {
        selectAllCheckbox.click();
        expect(selectAllCheckbox.indeterminate).to.be.false;

        firstBodyCheckbox.checked = false;
        expect(selectAllCheckbox.indeterminate).to.be.true;
      });

      it('should have indeterminate false if selectedItems contains all items, no matter the order', () => {
        grid.set('selectedItems', ['baz', 'foo', 'bar', 'hi']);

        expect(selectionColumn.selectAll).to.be.true;
        expect(selectAllCheckbox.indeterminate).to.be.false;
      });

      it('should have select-all false if selectedItems does not contain all items', () => {
        selectAllCheckbox.click();
        expect(selectionColumn.selectAll).to.be.true;
        expect(selectAllCheckbox.indeterminate).to.be.false;

        grid.set('selectedItems', ['baz', 'foo', 'hi']);
        expect(selectionColumn.selectAll).to.be.false;
        expect(selectAllCheckbox.indeterminate).to.be.true;
      });

      it('should not have selectAll after selecting a single item', () => {
        firstBodyCheckbox.checked = true;

        expect(selectionColumn.selectAll).to.be.false;
      });

      it('should not have selectAll after deselecting a single item', () => {
        selectAllCheckbox.click();

        firstBodyCheckbox.checked = false;

        expect(selectionColumn.selectAll).to.be.false;
      });

      it('should have selectAll after selecting all manually', () => {
        selectAllCheckbox.click();
        firstBodyCheckbox.checked = true;

        firstBodyCheckbox.checked = true;

        expect(selectionColumn.selectAll).to.be.true;
        expect(selectAllCheckbox.indeterminate).to.be.false;
      });

      it('should not reassign selectedItems array when selecting all manually', () => {
        selectAllCheckbox.click();
        firstBodyCheckbox.checked = false;

        const details = [];
        const spy = sinon.spy(e => {
          // event object gets reused so need to store the details before asserting.
          details.push(e.detail);
        });
        grid.addEventListener('selected-items-changed', spy);

        firstBodyCheckbox.checked = true;

        expect(details.length).to.eql(2); // should have 3rd event with path 'selectedItems'
        expect(details[0].path).to.eql('selectedItems.splices');
        expect(details[1].path).to.eql('selectedItems.length');
      });

      it('should select-all when all items are selected', () => {
        rows.forEach(row => {
          const cellContent = getCellContent(getRowCells(row)[0]);
          const checkbox = cellContent.children[0];
          checkbox.checked = true;
        });

        expect(selectionColumn.selectAll).to.be.true;
        expect(selectAllCheckbox.indeterminate).to.be.false;
      });

      it('should deselect-all when all items are deselected', () => {
        selectAllCheckbox.click();

        rows.forEach(row => {
          const cellContent = getCellContent(getRowCells(row)[0]);
          const checkbox = cellContent.children[0];
          checkbox.checked = false;
        });

        expect(selectionColumn.selectAll).to.be.false;
        expect(selectAllCheckbox.indeterminate).to.be.false;
      });

      it('should update select all when filters change', () => {
        grid.items = [{value: 'foo'}, {value: 'bar'}];

        firstBodyCheckbox.checked = true;

        const filter = grid.querySelector('#filter');
        filter.value = 'foo';
        Polymer.Base.fire('input', {}, {node: filter});

        filter.parentElement._debouncerFilterChanged.flush();
        expect(selectionColumn.selectAll).to.be.true;
      });

      it('should not throw with data provider', () => {
        grid.items = undefined;
        grid.dataProvider = infiniteDataProvider;
        grid.size = 1;

        const checkbox = getBodyCellContent(grid, 0, 0).firstElementChild;
        expect(() => checkbox.click()).not.to.throw(Error);
      });

      it('should override the header content with header text', () => {
        selectionColumn.header = 'foo';
        expect(firstHeaderCellContent.textContent).to.equal('foo');
        expect(firstBodyCellContent.firstElementChild).to.equal(firstBodyCheckbox);
      });

      it('should override the header content with a header renderer', () => {
        selectionColumn.headerRenderer = root => root.textContent = 'foo';
        expect(firstHeaderCellContent.textContent).to.equal('foo');
        expect(firstBodyCellContent.firstElementChild).to.equal(firstBodyCheckbox);
      });

      it('should override the body content with path', () => {
        selectionColumn.path = 'length';
        expect(firstBodyCellContent.textContent).to.equal('3');
        expect(firstHeaderCellContent.firstElementChild).to.equal(selectAllCheckbox);
      });

      it('should override the body content with a renderer', () => {
        selectionColumn.renderer = root => root.textContent = 'foo';
        expect(firstBodyCellContent.textContent).to.equal('foo');
        expect(firstHeaderCellContent.firstElementChild).to.equal(selectAllCheckbox);
      });
    });
  </script>

</body>

</html>
